/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 *
 * DSS is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 *
 * dsscmd_connection.c
 *
 *
 * IDENTIFICATION
 *    src/cmd/dsscmd_connection.c
 *
 * -------------------------------------------------------------------------
 */

#include "dss_api_impl.h"
#include "dss_interaction.h"
#include "dsscmd.h"
#include "dss_malloc.h"
#include "dss_file.h"
#include "dsscmd_interactive.h"
#include "dsscmd_connection.h"

#define DSS_SUBSTR_UDS_PATH "UDS:"

typedef struct {
    dss_conn_t conn;
    char server_locator[DSS_MAX_PATH_BUFFER_SIZE];
    char dss_home_last[DSS_MAX_PATH_BUFFER_SIZE];
} dss_cmd_of_uds_conn_t;

static dss_cmd_of_uds_conn_t *g_dss_uds_conn = NULL;

void dss_cmd_disconnect_exit()
{
    if (g_dss_uds_conn != NULL) {
        dss_disconnect_ex(&g_dss_uds_conn->conn);
        CM_FREE_PTR(g_dss_uds_conn);
        dss_free_vg_info();
    }
}

status_t get_default_server_locator(char *server_locator)
{
    char name[CM_MAX_PATH_LEN] = "LSNR_PATH";
    char *value = NULL;
    status_t status = dss_get_cfg_param_by_cfg(dss_cmd_get_inst_cfg(), name, &value);
    DSS_RETURN_IFERR2(status, DSS_PRINT_ERROR("Failed to get cfg param item(%s), status(%d).\n", name, status));
    const size_t PATH_SIZE = DSS_MAX_PATH_BUFFER_SIZE;
    int ret = snprintf_s(server_locator, PATH_SIZE, PATH_SIZE - 1, "UDS:%s/.dss_unix_d_socket", value);
    DSS_RETURN_IFERR2(ret < 0 ? CM_ERROR : CM_SUCCESS, DSS_PRINT_ERROR("Failed to sprintf to server_locator.\n"));
    return CM_SUCCESS;
}

status_t get_specified_server_locator(const char *input_args, char *server_locator)
{
    status_t status = strcpy_s(server_locator, DSS_MAX_PATH_BUFFER_SIZE, input_args);
    DSS_RETURN_IFERR2(status, DSS_PRINT_ERROR("Failed(%d) to strcpy_s of server_locator.\n", status));
    return CM_SUCCESS;
}

status_t get_server_locator(const char *uds_path, char *server_locator)
{
    if (uds_path != NULL) {
        return get_specified_server_locator(uds_path, server_locator);
    }
    return get_default_server_locator(server_locator);
}

static status_t dss_uds_set_up_connection(const char *server_locator, dss_cmd_of_uds_conn_t *dss_conn)
{
    if (strlen(server_locator) <= strlen(DSS_SUBSTR_UDS_PATH)) {
        LOG_DEBUG_ERR("Error server locator format of UDS\n");
        return CM_ERROR;
    }
    const char *server_path = (const char *)(server_locator + strlen(DSS_SUBSTR_UDS_PATH));
    if (server_path[0] == '~') {
        const char *sys_home_path = getenv(SYS_HOME);
        char abs_server_path[DSS_MAX_PATH_BUFFER_SIZE];
        const size_t PATH_SIZE = DSS_MAX_PATH_BUFFER_SIZE;
        int32 ret = snprintf_s(abs_server_path, PATH_SIZE, PATH_SIZE - 1, "UDS:%s%s", sys_home_path, server_path + 1);
        if (ret < 0) {
            LOG_RUN_ERR("Failed(%d) to snprintf_s when convert relative path to absolute", ret);
            return CM_ERROR;
        }
        status_t status = dss_connect_ex((const char *)abs_server_path, NULL, &dss_conn->conn);
        if (status != CM_SUCCESS) {
            LOG_DEBUG_ERR("Failed to set up connect(url:%s)\n", abs_server_path);
            return status;
        }
    } else {
        status_t status = dss_connect_ex(server_locator, NULL, &dss_conn->conn);
        if (status != CM_SUCCESS) {
            LOG_DEBUG_ERR("Failed to set up connect(url:%s)\n", server_locator);
            return status;
        }
    }
    return CM_SUCCESS;
}

status_t dss_get_connection_opt_inner(const char *server_locator)
{
    size_t size = sizeof(dss_cmd_of_uds_conn_t);
    g_dss_uds_conn = cm_malloc(size);
    if (g_dss_uds_conn == NULL) {
        DSS_PRINT_ERROR("Failed to malloc space for g_dss_uds_conn.\n");
        return CM_ERROR;
    }
    status_t status = memset_s(g_dss_uds_conn, size, 0, size);
    securec_check_ret(status);

    size = sizeof(char) * DSS_MAX_PATH_BUFFER_SIZE;
    status = strcpy_s(g_dss_uds_conn->server_locator, size, server_locator);
    DSS_RETURN_IFERR2(status, DSS_PRINT_ERROR("Failed to strcpy server_locator to g_dss_uds_conn.\n"));

    status = dss_uds_set_up_connection(g_dss_uds_conn->server_locator, g_dss_uds_conn);
    DSS_RETURN_IF_ERROR(status);

    size = sizeof(char) * DSS_MAX_PATH_BUFFER_SIZE;
    status = memcpy_s(g_dss_uds_conn->dss_home_last, size, dss_get_env()->inst_cfg.home, size);
    securec_check_ret(status);
    return CM_SUCCESS;
}

bool8 dss_cmd_is_connected()
{
    return g_dss_uds_conn != NULL;
}

status_t dss_cmd_check_reload_config(const char *input_home)
{
    if (input_home != NULL) {
        return CM_SUCCESS;
    }
    const char *dss_home = getenv(DSS_ENV_HOME);
    if (dss_home == NULL) {
        DSS_PRINT_ERROR("Environment variant DSS_HOME not found!\n");
        return CM_ERROR;
    }
    dss_config_t *inst_cfg = dss_cmd_get_inst_cfg();
    const char *home_cfg = dss_get_cfg_dir(inst_cfg);
    if (strcmp(home_cfg, dss_home) != 0) {
        status_t status = dss_set_cfg_dir(NULL, inst_cfg);
        DSS_RETURN_IFERR2(status, DSS_PRINT_ERROR("Environment variant DSS_HOME not found!\n"));
        status = dss_load_local_server_config(inst_cfg);
        DSS_RETURN_IFERR2(status, DSS_PRINT_ERROR("Failed to load local server config, status(%d)!\n", status));
    }
    return CM_SUCCESS;
}

status_t dss_cmd_check_is_conn_valid(const char *input_home, const char *input_uds)
{
    /* check dss_home is valid */
    const char *cur_home = (input_home == NULL) ? getenv(DSS_ENV_HOME) : input_home;
    if (cur_home == NULL) {
        DSS_PRINT_ERROR("Environment variant DSS_HOME not found!\n");
        return CM_ERROR;
    }
    if (strcmp(cur_home, g_dss_uds_conn->dss_home_last) != 0) {
        return CM_ERROR;
    }
    /* check server_locator is valid */
    char server_locator[DSS_MAX_PATH_BUFFER_SIZE] = {0};
    status_t status = get_default_server_locator(server_locator);
    DSS_RETURN_IF_ERROR(status);
    const char *cur_uds = (input_uds == NULL) ? server_locator : input_uds;
    const char *pre_uds = g_dss_uds_conn->server_locator;
    if (strcmp(cur_uds, pre_uds) != 0) {
        return CM_ERROR;
    }
    return CM_SUCCESS;
}

dss_conn_t *dss_get_connection_common(const char *input_home, const char *input_uds)
{
    if (dss_cmd_is_connected()) {
        if (dss_cmd_check_is_conn_valid(input_home, input_uds) == CM_SUCCESS) {
            return &g_dss_uds_conn->conn;
        } else {
            dss_cmd_disconnect_exit();
        }
    }

    if (dss_cmd_check_reload_config(input_home) != CM_SUCCESS) {
        return NULL;
    }

    char server_locator[DSS_MAX_PATH_BUFFER_SIZE] = {0};
    if (get_server_locator(input_uds, server_locator) != CM_SUCCESS) {
        return NULL;
    }

    if (dss_get_connection_opt_inner(server_locator) != CM_SUCCESS) {
        dss_cmd_disconnect_exit();
        return NULL;
    }
    return &g_dss_uds_conn->conn;
}

dss_conn_t *dss_get_connection_by_uds(const char *input_uds)
{
    return dss_get_connection_common(NULL, input_uds);
}
